package langnstats.project.tools;


import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Collections;

public class CountMap<K> extends HashMap<K,Integer> {
	public static final long serialVersionUID = 1;
	
	public CountMap(){ super(); }
	public CountMap(int initialCapacity){ super(initialCapacity); }
	public CountMap(int initialCapacity, float loadFactor){ super(initialCapacity, loadFactor); }
	public CountMap(Map<K,Integer> map){ super(map); }
	
	
	
	
	protected class CountMapEntryComparator implements Comparator<Map.Entry<K,Integer>>{
		public int compare(Map.Entry<K,Integer> e1, Map.Entry<K,Integer> e2){
			return e1.getValue()-e2.getValue();
		}		
	};
	protected class CountMapEntryReverseComparator implements Comparator<Map.Entry<K,Integer>>{
		public int compare(Map.Entry<K,Integer> e1, Map.Entry<K,Integer> e2){
			CountMapEntryComparator comparator = new CountMapEntryComparator();
			return -comparator.compare(e1, e2);
		}		
	};
	
	public List<K> createSortedList(boolean smallToBig){
		List<Map.Entry<K,Integer>> list = new ArrayList<Map.Entry<K,Integer>>(this.entrySet());
		
		Comparator<Map.Entry<K,Integer>> c = null;
		if(smallToBig){ c = new CountMapEntryComparator(); }
		else{ c = new CountMapEntryReverseComparator(); }
		
		Collections.sort(list, c);
		List<K> returnList = new ArrayList<K>();
		
		for(Map.Entry<K,Integer> entry : list){ returnList.add(entry.getKey()); }
		return returnList;
	}
	
	public Map<K,Double> getProportionMap(){
		Map<K,Double> returnMap = new HashMap<K,Double>();
		int totalCount = this.getTotalCount();
		
		for(Map.Entry<K, Integer> entry : this.entrySet()){
			returnMap.put(entry.getKey(), ((double)entry.getValue())/totalCount);
		}
		return returnMap;
	}
	
	public int getTotalCount(){
		int totalCount = 0;
		for(Map.Entry<K, Integer> entry : this.entrySet()){
			totalCount += entry.getValue();
		}
		return totalCount;
	}
	public void increCount(K k){ addCount(k,1); }
	public void addCount(Map.Entry<K,Integer> entry){
		this.addCount(entry.getKey(), entry.getValue());
	}
	public void addCount(K k, int i){
		int prevCount = 0;
		if(this.containsKey(k)){
			prevCount = this.get(k);
			this.remove(k);
		}
		this.put(k, prevCount+i);
	}
	
	public void increCountAll(K[] c){
		for(K k : c){ increCount(k); }
	}
	public void increCountAll(Collection<? extends K> c){
		for(K k : c){ increCount(k); }
	}
	public void addCountAll(Map<? extends K, Integer> map){
		for(Map.Entry<? extends K,Integer> entry : map.entrySet()){
			K key = entry.getKey();
			int i = entry.getValue();
			this.addCount(key,i);			
		}
	}
	
	public void decreCount(K k){
		if(!this.containsKey(k) || (this.get(k)<=0) ){
			throw new NullPointerException("Key, such as '"+k+"' doesn't exist!");
		}
		
		int newCount = this.getCount(k)-1;
		this.remove(k);		 
		
		if(newCount<=0){ return; }	// keep the key removed
		else{ this.put(k, newCount); }		
	}
	
	public int getCount(K k){ return this.get(k); }
	
	public double entropy(){
		Collection<Integer> countsInteger = this.values();
		
		int[] counts = new int[countsInteger.size()];
		Iterator<Integer> iterator = countsInteger.iterator();
		for(int i=0; iterator.hasNext(); i++){ counts[i] = iterator.next(); }
		
		return StatisticsToolkit.entropy(counts);
	}
}